unit DataContainer;

interface

uses ifmmemoryunit, ifmsysutilsunit, classes, strutils, sysutils, math, contnrs;

type
   DataSourceVar = (
      dsObs,
      dsMod,
      dsPhi,
      dsCblFac,
      dsCblPow,
      dsWeight,
      dsResU,
      dsResW,
      dsNFromDS,
      dsObsRate,
      dsModRate,
      dsObsSorted,
      dsModSorted,
      dsNObsLessThanMod,
      dsFirst = dsObs,
      dsLast = dsNObsLessThanMod);
   DataSourceVarArray = array of DataSourceVar;


(*   YearClassVar = (
      yrNRate,
      yrFRate,
      yrObsRate,
      yrModRate,
      yrNFromCatchRate
   );*)

(*   YearClassContainer = class
   private
      m_YearclassData : TObjectList;
   public
      constructor Create;
      destructor Destroy; override;
      property YearclassData : TObjectList read m_YearclassData;
   end;*)



   DataSourceContainer = class
   private
      m_DsData : TObjectList;
      m_Key, m_Name : string;
      m_FirstYear, m_FirstAge : Integer;
      m_KolCriteria : double;
   public
      constructor Create;
      destructor Destroy; override;
      property DsData : TObjectList read m_DSData;
      property Key : string read m_Key write m_Key;
      property Name : string read m_Name write m_Name;
      property FirstYear : Integer read m_FirstYear;
      property FirstAge : Integer read m_FirstAge;
      property KolCriteria : double read m_KolCriteria;
   end; // DataSourceContainer

   StdVar = (
      stdN = 0,
      stdF,
      stdM,
      stdNRate,
      stdFRate,

      stdRecruits,
      stdTotBM,
      stdSSB,
      stdFMean,
      stdLandings,

      stdBootSSB,
      stdBootFMean,
      stdBootObj,

      stdBootSSBOrg,
      stdBootFMeanOrg,
      stdBootObjOrg,

      stdBootSSBLastYear,
      stdBootFMeanLastYear,

      stdRetroSSB,
      stdRetroFMean,
      stdRetroRecruit
      );
   TasacsDataContainer = class
   private
      m_FirstYear, m_FirstAge : Integer;
      m_RunPath : string;
      m_StdData : TObjectList;
      m_Ds : TObjectList;
      m_Error : string;
      m_RunId, m_Process, m_Model : string;
  //    m_YearClasses : TObjectList;
   public
      constructor Create(RunPath : string);
      destructor Destroy; override;
      function GetLastYear : Integer;
      function GetOldestAge : Integer;
      function GetLastAge : Integer;
      function GetNumYears : Integer;
      function GetNumAges : Integer;
      function GetNumTrueAges : Integer;
      function GetNumCohorts : Integer;
      function GetNumYearClassDiagrams : Integer;
      function GetNumYearClassChartsPerDiagram : Integer;
      function GetNumElmsInCohort(iCohort : Integer) : Integer;
      function GetBirthYearFromCohort(iCohort : Integer) : Integer;
      function GetRecruitYearFromCohort(iCohort : Integer) : Integer;
      function GetFirstAgeIndexInCohort(iCohort : Integer) : Integer;
      function GetFirstYearIndexInCohort(iCohort : Integer) : Integer;
      property FirstYear : Integer read m_FirstYear;
      property FirstAge : Integer read m_FirstAge;
      property LastYear : Integer read GetLastYear;
      property OldestAge : Integer read GetOldestAge;
      property LastAge : Integer read GetLastAge;
      property NumAges : Integer read GetNumAges;
      property NumTrueAges : Integer read GetNumTrueAges;
      property NumYears : Integer read GetNumYears;
      property NumCohorts : Integer read GetNumCohorts;
      property NumYearClassDiagrams : Integer read GetNumYearClassDiagrams;
      property NumYearClassChartsPerDiagram : Integer read GetNumYearClassChartsPerDiagram;
      property NumElmsInCohort[iCohort : Integer] : Integer read GetNumElmsInCohort;
      property BirthYearFromCohort[iCohort : Integer] : Integer read GetBirthYearFromCohort;
      property RecruitYearFromCohort[iCohort : Integer] : Integer read GetRecruitYearFromCohort;
      property FirstAgeIndexInCohort[iCohort : Integer] : Integer read GetFirstAgeIndexInCohort;
      property FirstYearIndexInCohort[iCohort : Integer] : Integer read GetFirstYearIndexInCohort;
      property StdData : TObjectList read m_StdData;
      property Ds : TObjectList read m_Ds;
      property Error : string read m_Error;
      property Model : string read m_Model;
      property RunId : string read m_RunId;
   end;



implementation


constructor DataSourceContainer.Create;
var
   I : DataSourceVar;
begin
   m_DSData := TObjectList.Create;
   for I := dsFirst to dsLast do
      case I of
         dsObs..dsModRate:
            m_DsData.Add(FloatMatrix.Create());
         dsObsSorted..dsNObsLessThanMod:
            m_DsData.Add(FloatArray.Create());
      end; // case
end; // DataSourceContainer.Create


destructor DataSourceContainer.Destroy;
begin
   m_DsData.Free;
   inherited;
end; // DataSourceContainer.Destroy


procedure ConvertToRates(ConvertFrom, ConvertTo : FloatMatrix);
var
   Num_Ages, Num_Years, iCohort, NumCohorts, iYearEnd, iAgeEnd, iYear, iAge : Integer;
   MaxVal : double;
begin
   Num_Ages := ConvertFrom.GetSize(2);
   Num_Years := ConvertFrom.GetSize(1);

   if (Num_Years = 0) then
      exit;
   if (Num_Ages = 0) then
      exit;

   NumCohorts := Num_Ages + Num_Years - 1;
   for iCohort := 0 to NumCohorts - 1 do
   begin
      // Find the indexes at the end of the cohort, starting with the youngest one.
      if (iCohort <= Num_Ages - 1) then
      begin
         iYearEnd := Num_Years - 1;
         iAgeEnd := iCohort;
      end
      else
      begin
         iAgeEnd := Num_Ages - 1;
         iYearEnd := Num_Years - 1 - (iCohort - Num_Ages + 1);
      end;

      MaxVal := -maxdouble;
      iYear := iYearEnd;
      iAge := iAgeEnd;
      repeat
         MaxVal := max(ConvertFrom[iYear, iAge], MaxVal);
         if ((iYear = 0) or (iAge = 0)) then
            break; // Cohort finished
         // Prepare for the next element in the cohort
         iYear := iYear - 1;
         iAge := iAge - 1;
      until false;
      iYear := iYearEnd;
      iAge := iAgeEnd;
      repeat
         if MaxVal <> 0 then
            ConvertTo[iYear, iAge] := ConvertFrom[iYear, iAge] / MaxVal
         else
            ConvertTo[iYear, iAge] := 0;
         if ((iYear = 0) or (iAge = 0)) then
            break; // Cohort finished
         // Prepare for the next element in the cohort
         iYear := iYear - 1;
         iAge := iAge - 1;
      until false
   end; // for

end; // ConvertToRates

const c_BootFiles : array[1..3] of string = ('Boot_SSB.txt', 'Boot_FMean.txt', 'Boot_ObjFunc.txt');
const c_RetroFiles : array[1..3] of string = ('Retro_SSB.txt', 'Retro_FMean.txt', 'Retro_Recruit.txt');


const c_CurrentVersion = 1.2;


constructor TasacsDataContainer.Create(RunPath : string);
var
   f : TextFile;
   s : string;
   sl : Tstringlist;
   iYear, iAge, iVar, NumAges, NumYears : Integer;
   val : double;
   I : StdVar;
   ds : DataSourceVar;
   dsc : DataSourceContainer;
   resUval, resWVal, obsval, modval, weight, KolSize, BiggestKolSize : double;
   BootFile, RetroFile : string;
   iMod : Integer;
   NMatrix, CanumMatrix : FloatMatrix;
   C, M, _F, Z, NFromCatch : double;
   iObsYear, iObsAge, iDs : Integer;
   iRun, iBootFile, iRetroFile, maxcount, iSorted : Integer;
   FileVersion : double;
begin
   m_RunPath := RunPath;
   sl := TStringList.Create;
   // Initialize containers
   m_StdData := TObjectList.Create;
   m_Ds := TobjectList.Create;
//   m_YearClasses := TObjectList.Create;
   for I := stdN to stdRetroRecruit do
      case I of
         stdN..stdFRate, stdBootSSB, stdBootFMean, stdRetroSSB..stdRetroRecruit:
            m_StdData.Add(FloatMatrix.Create);
      else
         m_StdData.Add(FloatArray.Create);
      end; // case

   m_Error := '';
   // Read result file
   if not DirectoryExists(m_RunPath) then
   begin
      m_Error := 'The run directory doesnt exist: "' + m_RunPath + '"';
      exit;
   end;
   if not FileExists(m_RunPath + '\Summary.txt') then
   begin
      m_Error := 'The file doesnt exist: "' + m_RunPath + '\Summary.txt"';
      exit;
   end;
   AssignFile(f, m_RunPath + '\Summary.txt');
   reset(f);
   readln(f, s); // Heading
   readln(f, m_RunId);
   readln(f, m_Process);
   readln(f, m_Model);
   readln(f);
   for I := stdN to stdM do
   begin
      readln(f, s); // Heading
      FloatMatrix(m_StdData[ord(I)]).ArrayName := trim(s);
      readln(f, s); // [Year 1..N]
      sl.DelimitedText := trim(s);
      m_FirstYear := round(safestrtofloat(sl[0]));
      iAge := 0;
      repeat
         readln(f, s); // [Age, Value 1..N]
         sl.DelimitedText := trim(s);
         if iAge = 0 then
            m_FirstAge := round(safestrtofloat(sl[0]));
         for iYear := 1 to sl.Count - 1 do
         begin
            val := safestrtofloat(sl[iYear]);
            FloatMatrix(m_StdData[ord(I)])[iYear - 1, iAge] := val;
         end;
         iAge := iAge + 1;
      until trim(s) = '';
   end;

   // Read Summary
   readln(f, s); // "Summary:"
   readln(f, s); // [Headings]
   for I := stdRecruits to stdLandings do
      FloatArray(m_StdData[ord(I)]).ArrayName := trim(midstr(s, 4 + 15 * (ord(I)-ord(stdRecruits)) + 1, 15));
   iYear := 0;
   repeat
      readln(f, s); // [Year, Variable 1..N]
      if trim(s) <> '' then
      begin
         sl.DelimitedText := trim(s);
         for I := stdRecruits to stdLandings do
         begin
            val := safestrtofloat(sl[ord(I) - ord(stdRecruits) + 1]);
            if val = -1 then
               val := nan;
            FloatArray(m_StdData[ord(I)])[iYear] := val;
         end;
         iYear := iYear + 1;
      end;
   until trim(s) = '';
   CloseFile(f);

   for iBootFile := 1 to 3 do
   begin
      // Read bootstrap file
      BootFile := m_RunPath + '\' + c_BootFiles[iBootFile];
      if not FileExists(BootFile) then continue;
      AssignFile(f, BootFile);
      reset(f);
      readln(f, s); // Title
      readln(f, s); // Run id
      readln(f, s); // Comment
      iRun := 0;
      repeat
         if eof(f) then break;
         readln(f, s); // [Age, Value 1..N]
         sl.DelimitedText := trim(s);
         for iYear := 1 to sl.Count - 1 do
         begin
            val := safestrtofloat(sl[iYear]);
            case iBootFile of
               1..2:
               begin
                  case iRun of
                     0:
                        FloatArray(m_StdData[ord(stdBootSSBOrg) + iBootFile - 1])[iYear - 1] := val
                  else
                     FloatMatrix(m_StdData[ord(stdBootSSB) + iBootFile - 1])[iRun - 1, iYear - 1] := val;
                  end; // case
               end
            else
               case iRun of
                  0:
                     FloatArray(m_StdData[ord(stdBootObjOrg)])[0] := val;
               else
                  FloatArray(m_StdData[ord(stdBootObj)])[iRun - 1] := val;
               end;
            end; // case
         end;
         iRun := iRun + 1;
      until trim(s) = '';
      CloseFile(f);
   end; //
   for iRun := 0 to FloatMatrix(m_StdData[ord(stdBootSSB)]).GetSize(1) - 1 do
   begin
      // Extract SSB - FMean pairs from last assessment year
      FloatArray(m_StdData[ord(stdBootSSBLastYear)])[iRun] :=
         FloatMatrix(m_StdData[ord(stdBootSSB)])[iRun, FloatMatrix(m_StdData[ord(stdBootSSB)]).GetSize(2) - 2];
      FloatArray(m_StdData[ord(stdBootFMeanLastYear)])[iRun] :=
         FloatMatrix(m_StdData[ord(stdBootFMean)])[iRun, FloatMatrix(m_StdData[ord(stdBootFMean)]).GetSize(2) - 2];
   end;
   FloatMatrix(m_StdData[ord(stdBootSSB)]).SortColumns;
   FloatMatrix(m_StdData[ord(stdBootFMean)]).SortColumns;
   FloatArray(m_StdData[ord(stdBootObj)]).Sort;

   // Read retro files
   for iRetroFile := 1 to 3 do
   begin
      RetroFile := m_RunPath + '\' + c_RetroFiles[iRetroFile];
      if not FileExists(RetroFile) then continue;
      AssignFile(f, RetroFile);
      reset(f);
      readln(f, s); // Title
      readln(f, s); // Run id
      readln(f, s); // Comment
      iRun := 0;
      maxcount := 0;
      repeat
         if eof(f) then break;
         readln(f, s); // [Age, Value 1..N]
         sl.DelimitedText := trim(s);
         maxcount := max(sl.Count, maxcount);
         for iYear := 0 to maxcount - 1 do
         begin
            if iYear <= sl.Count - 1 then
               val := safestrtofloat(sl[iYear])
            else
               val := nan;
            FloatMatrix(m_StdData[ord(stdRetroSSB) + iRetroFile - 1])[iRun, iYear] := val;
         end;
         iRun := iRun + 1;
      until trim(s) = '';
      CloseFile(f);
   end; //

   // Read Output file
   AssignFile(f, m_RunPath + '\Output.txt');
   reset(f);
   readln(f, s); // First line
   if (length(s) >= 7) and (leftstr(s, 3) = 'VER') then
   begin
      FileVersion := roundto(safestrtofloat(trim(RightStr(s, length(s) - 3))), -1);
      readln(f, s); // Header
   end
   else
      FileVersion := 1.1;

   readln(f, m_RunId);
   readln(f);
   while not eof(f) do
   begin
      dsc := DataSourceContainer.Create;
      m_Ds.Add(dsc);
      with dsc do
      begin
         readln(f, s);
         Key := trim(s);
         readln(f, s);
         Name := trim(s);
         // Read data source info
         for ds := dsObs to dsNFromDS do
         begin
            if (ds = dsCblPow) and ((FileVersion < c_CurrentVersion) or (m_Ds.count = 1)) then
               continue;
            readln(f, s); // Variable heading
            readln(f, s); // [Year 1..N]
            sl.DelimitedText := trim(s);
            m_FirstYear := round(safestrtofloat(sl[0]));
            iAge := 0;
            repeat
               readln(f, s); // [Age, Value 1..N]
               if trim(s) <> '' then
               begin
                  sl.DelimitedText := trim(s);
                  if iAge = 0 then
                     m_FirstAge := round(safestrtofloat(sl[0]));
                  for iYear := 1 to sl.Count - 1 do
                  begin
                     val := safestrtofloat(sl[iYear]);
                     FloatMatrix(m_DsData[ord(ds)])[iYear - 1, iAge] := val;
                  end;
                  iAge := iAge + 1;
               end;
            until trim(s) = '';
         end;
         // Calculate unweighted reiduals
{         NumYears := FloatMatrix(m_DsData[ord(dsObs)]).GetSize(1);
         NumAges := FloatMatrix(m_DsData[ord(dsObs)]).Getsize(2);
         for iYear := 0 to NumYears - 1 do
            for iAge := 0 to NumAges - 1 do
            begin
               obsval := FloatMatrix(m_DsData[ord(dsObs)])[iYear, iAge];
               modval := FloatMatrix(m_DsData[ord(dsMod)])[iYear, iAge];
               if (obsval > 0) and (modval > 0) then
                  resUval := ln(obsval / modval)
               else
                  resUval := 0;
               FloatMatrix(m_DsData[ord(dsResU)])[iYear, iAge] := resUval;
               weight := FloatMatrix(m_DsData[ord(dsWeight)])[iYear, iAge];
               resWVal := resUVal * weight;
               FloatMatrix(m_DsData[ord(dsResW)])[iYear, iAge] := resWval;
            end;
}
         if (FloatMatrix(m_DsData[ord(dsObs)]).GetSize(1) <> FloatMatrix(m_DsData[ord(dsMod)]).GetSize(1)) or
            (FloatMatrix(m_DsData[ord(dsObs)]).GetSize(2) <> FloatMatrix(m_DsData[ord(dsMod)]).GetSize(2)) then
            continue;
         if (FloatMatrix(m_DsData[ord(dsWeight)]).GetSize(1) <> FloatMatrix(m_DsData[ord(dsMod)]).GetSize(1)) or
            (FloatMatrix(m_DsData[ord(dsWeight)]).GetSize(2) <> FloatMatrix(m_DsData[ord(dsMod)]).GetSize(2)) then
            continue;

         NumYears := FloatMatrix(m_DsData[ord(dsObs)]).GetSize(1);
         NumAges := FloatMatrix(m_DsData[ord(dsObs)]).Getsize(2);
         iSorted := 0;
         for iYear := 0 to NumYears - 1 do
            for iAge := 0 to NumAges - 1 do
            begin
               obsval := FloatMatrix(m_DsData[ord(dsObs)])[iYear, iAge];
               modval := FloatMatrix(m_DsData[ord(dsMod)])[iYear, iAge];
               weight := FloatMatrix(m_DsData[ord(dsWeight)])[iYear, iAge];
               if ((weight <= 0) or (obsval <= 0) or (modval <= 0)) then
                  continue;
               FloatArray(m_DsData[ord(dsObsSorted)])[iSorted] := obsval;
               FloatArray(m_DsData[ord(dsModSorted)])[iSorted] := modval;
               iSorted := iSorted + 1;
            end;
//         FloatMatrix(m_DsData[ord(dsObs)]).GetFloatArray(FloatArray(m_DsData[ord(dsObsSorted)]));
//         FloatMatrix(m_DsData[ord(dsMod)]).GetFloatArray(FloatArray(m_DsData[ord(dsModSorted)]));
         FloatArray(m_DsData[ord(dsObsSorted)]).Sort;
         FloatArray(m_DsData[ord(dsModSorted)]).Sort;
//         FloatArray(m_DsData[ord(dsModSorted)]).RemoveLeftElms(-1);
//         FloatArray(m_DsData[ord(dsModSorted)]).RemoveLeftElms(0);
         BiggestKolSize := 0;
         with FloatArray(m_DsData[ord(dsModSorted)]) do
         for iMod := 0 to FloatArray(m_DsData[ord(dsModSorted)]).Capacity - 1 do
         begin
            FloatArray(m_DsData[ord(dsNObsLessThanMod)])[iMod] :=
               FloatArray(m_DsData[ord(dsObsSorted)]).NumElmsInRange(0.0000001,
               FloatArray(m_DsData[ord(dsModSorted)])[iMod] - 0.0000001);
            KolSize := abs(FloatArray(m_DsData[ord(dsNObsLessThanMod)])[iMod] - iMod);
            BiggestKolSize := max(KolSize, BiggestKolSize);
         end;
         m_KolCriteria := BiggestKolSize / FloatArray(m_DsData[ord(dsModSorted)]).Capacity
      end;
   end;
   CloseFile(f);

   sl.Free;

   FloatMatrix(m_StdData[ord(stdNRate)]).ArrayName := 'N rate';
   FloatMatrix(m_StdData[ord(stdFRate)]).ArrayName := 'F rate';

   NMatrix := FloatMatrix(m_StdData[ord(stdN)]);
   for iYear := 0 to NMatrix.GetSize(1) - 1 do
      for iAge := 0 to NMatrix.GetSize(2) - 1 do
      begin
{         dsc := DataSourceContainer(m_Ds[0]);
         CanumMatrix := FloatMatrix(dsc.DsData[0]);
         iObsYear := iYear + m_FirstYear - dsc.FirstYear;
         iObsAge := iAge + m_FirstAge - dsc.FirstAge;
         C := 0;
         if (0 <= iObsYear) and (iObsYear <= CanumMatrix.GetSize(1) - 1) and
            (0 <= iObsAge) and (iObsAge <= CanumMatrix.GetSize(2) - 1) then
            C := CanumMatrix[iObsYear, iObsAge];
         M := FloatMatrix(m_StdData[ord(stdM)])[iYear, iAge];
         _F := FloatMatrix(m_StdData[ord(stdF)])[iYear, iAge];
         Z := M + _F;
         NFromCatch := 0;
         if _F > 0 then
            NFromCatch := C * Z / _F / (1 - Exp(-Z));
         FloatMatrix(m_StdData[ord(stdNFromCatch)])[iYear, iAge] := NFromCatch;}
         ConvertToRates(FloatMatrix(m_StdData[ord(stdN)]), FloatMatrix(m_StdData[ord(stdNRate)]));
         ConvertToRates(FloatMatrix(m_StdData[ord(stdF)]), FloatMatrix(m_StdData[ord(stdFRate)]));
         //ConvertToRates(FloatMatrix(m_StdData[ord(stdNFromCatch)]), FloatMatrix(m_StdData[ord(stdNFromCatchRate)]));
      end;
   for iDs := 0 to m_Ds.count - 1 do
   begin
      ConvertToRates(FloatMatrix(DataSourceContainer(m_Ds[iDs]).m_DsData[ord(dsObs)]), FloatMatrix(DataSourceContainer(m_Ds[iDs]).m_DsData[ord(dsObsRate)]));
      ConvertToRates(FloatMatrix(DataSourceContainer(m_Ds[iDs]).m_DsData[ord(dsMod)]), FloatMatrix(DataSourceContainer(m_Ds[iDs]).m_DsData[ord(dsModRate)]));
   end;
end;  // TasacsDataContainer.Create


destructor TasacsDataContainer.Destroy;
begin
   m_Ds.Free;
   m_StdData.Free;
//   m_YearClasses.Free;
   inherited;
end; // TasacsDataContainer.Destroy


function TasacsDataContainer.GetNumYears : Integer;
begin
   Result := FloatMatrix(m_StdData[ord(stdN)]).GetSize(1);
end; // TasacsDataContainer.GetNumYears


function TasacsDataContainer.GetNumAges : Integer;
begin
   Result := FloatMatrix(m_StdData[ord(stdN)]).GetSize(2);
end; // TasacsDataContainer.GetNumAges


function TasacsDataContainer.GetNumTrueAges : Integer;
begin
   Result := NumAges - 1;
end; // TasacsDataContainer.GetNumAges


function TasacsDataContainer.GetNumCohorts : Integer;
begin
   Result := NumYears + NumTrueAges - 1;
end; // TasacsDataContainer.GetNumCohorts


function TasacsDataContainer.GetNumYearClassDiagrams : Integer;
begin
   Result := ceil(NumCohorts / NumYearClassChartsPerDiagram);
end; // TasacsDataContainer.GetNumYearClassDiagrams


function TasacsDataContainer.GetNumYearClassChartsPerDiagram : Integer;
begin
   Result := 9;
end; // TasacsDataContainer.GetNumYearClassChartsPerDiagram


function TasacsDataContainer.GetLastYear : Integer;
begin
   Result := FirstYear + NumYears - 1;
end; // TasacsDataContainer.GetLastYear


function TasacsDataContainer.GetOldestAge : Integer;
begin
   Result := FirstAge + NumTrueAges - 1;
end; // TasacsDataContainer.GetOldestAge


function TasacsDataContainer.GetLastAge : Integer;
begin
   Result := FirstAge + NumAges - 1;
end; // TasacsDataContainer.GetLastAge


function TasacsDataContainer.GetNumElmsInCohort(iCohort : Integer) : Integer;
var
   Subtract, RecruitYear : Integer;
begin
   Subtract := 0;
   RecruitYear := RecruitYearFromCohort[iCohort];
   if RecruitYear < FirstYear then
      Subtract := FirstYear - RecruitYear
   else if RecruitYear + NumTrueAges - 1 > LastYear then
      Subtract := RecruitYear - (LastYear -  NumTrueAges + 1);
   Result := NumTrueAges - Subtract;
end; // TasacsDataContainer.GetNumElmsInCohort


function TasacsDataContainer.GetBirthYearFromCohort(iCohort : Integer) : Integer;
begin
   Result := FirstYear - OldestAge + iCohort;
end; // TasacsDataContainer.GetBirthYearFromCohort


function TasacsDataContainer.GetRecruitYearFromCohort(iCohort : Integer) : Integer;
begin
   Result := BirthYearFromCohort[iCohort] + FirstAge;
end; // TasacsDataContainer.GetRecruitYearFromCohort


function TasacsDataContainer.GetFirstAgeIndexInCohort(iCohort : Integer) : Integer;
begin
   if RecruitYearFromCohort[iCohort] < FirstYear then
      Result := NumTrueAges - NumElmsInCohort[iCohort]
   else
      Result := 0;
end; // TasacsDataContainer.GetFirstAgeIndexInCohort


function TasacsDataContainer.GetFirstYearIndexInCohort(iCohort : Integer) : Integer;
begin
   Result := RecruitYearFromCohort[iCohort] + FirstAgeIndexInCohort[iCohort] - FirstYear;
end; // TasacsDataContainer.GetFirstYearIndexInCohort


end.
